package net.chasing.retrofit.util;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

/**
 * Build.VERSION.RELEASE //string  用户系统环境对应的版本号
 * Build.MANUFACTURER // string 手机制作商
 * Build.MODEL // string 手机型号
 */
public class MobileInfo {
    private static final MobileInfo mMobileInfo = new MobileInfo();
    private final HashMap<String, Object> deviceInfo;

    private MobileInfo() {
        deviceInfo = new HashMap<>();
    }
    public static MobileInfo getInstance() {
        return mMobileInfo;
    }

    public static final String ANDROID_ID = "AndroidId";
    public static final String NET_TYPE = "netType";
    public static final String SECURECODE = "secureCode";
    public static final String MAC_ADDRESS = "macAddress";

    /**
     * 初始化设备信息
     * 获取手机信息
     */
    public void initDeviceInfo(Context ctx) {
        deviceInfo.put(ANDROID_ID, getDeviceUuid(ctx).toString());//string 手机唯一标识
        deviceInfo.put(NET_TYPE, getCurrentNetTypeInt(ctx)); //int
        deviceInfo.put(MAC_ADDRESS, getMacAddress()); //String
        deviceInfo.put(SECURECODE, getSecureCode(ctx));
    }

    public HashMap<String, Object> getDeviceInfo() {
        return deviceInfo;
    }

    //获取SHA1的值拼接上“;包名”
    @SuppressLint("PackageManagerGetSignatures")
    private String getSecureCode(Context ctx) {
        PackageInfo pInfo = null;
        try {
            pInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), PackageManager.GET_SIGNATURES);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        if (pInfo == null) return "";

        String secureCode = ";" + ctx.getPackageName();
        Signature[] signatures = pInfo.signatures;
        try {
            byte[] cert = signatures[0].toByteArray();
            InputStream input = new ByteArrayInputStream(cert);
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate c = (X509Certificate) cf.generateCertificate(input);
            MessageDigest md = MessageDigest.getInstance("SHA1");
            byte[] publicKey = md.digest(c.getEncoded());
            secureCode = byte2HexFormatted(publicKey) + secureCode;
        } catch (Exception e) {
            secureCode = "" + secureCode; //  "" 替换应用本身SHA1
            e.printStackTrace();
        }
        return secureCode;
    }

    private String byte2HexFormatted(byte[] arr) {
        StringBuilder str = new StringBuilder(arr.length * 2);
        for (int i = 0; i < arr.length; i++) {
            String h = Integer.toHexString(arr[i]);
            int l = h.length();
            if (l == 1)
                h = "0" + h;
            if (l > 2)
                h = h.substring(l - 2, l);
            str.append(h.toUpperCase());
            if (i < (arr.length - 1))
                str.append(':');
        }
        return str.toString();
    }

    /***
     * 通过AndroidId获取UUID
     * ANDROID_ID生成规则：签名+设备信息+设备用户
     * 在设备首次启动时，系统会随机生成一个64位的数字，
     * 并把这个数字以16进制字符串的形式保存下来，这个16进制的字符串就是ANDROID_ID，当设备被wipe后该值会被重置。
     * Android ID是一个不错的选择，64位的随机数重复率不高，而且不需要申请权限，
     * 但也有些小问题，比如有个很常见的Bug会导致设备产生相同的Android ID: 9774d56d682e549c，
     * 另外Android ID的生成不依赖硬件，恢复出厂设置改变Android ID
     * 此外：ANDROID_ID还具有以下缺点
     * i）现在网上已有修改设备ANDROID_ID值的APP应用。
     * ii) 某些厂商定制系统会导致不同的设备产生相同的ANDROID_ID或返回值为null。
     * iii) 对于CDMA制式的设备，ANDROID_ID和DeviceId返回的值相同
     */
    private UUID getDeviceUuid(Context context) {
        UUID uuid;
        @SuppressLint("HardwareIds") String androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
        if (TextUtils.isEmpty(androidId) || "9774d56d682e549c".equals(androidId)) {
            Log.i("android_id", "android id get failure generate uuid");
            SharedPreferences sharedPreferences = context.getSharedPreferences("objectCache", Context.MODE_PRIVATE);
            androidId = sharedPreferences.getString("androidId", "");
            if (TextUtils.isEmpty(androidId)) {
                androidId = UUID.randomUUID().toString().toLowerCase();
                SharedPreferences.Editor edit = sharedPreferences.edit();
                edit.putString("androidId", androidId);
                edit.apply();
            }
        } else {
            Log.i("android_id", "android id get success: " + androidId);
        }
        uuid = UUID.nameUUIDFromBytes(androidId.getBytes(StandardCharsets.UTF_8));
        return uuid;
    }

    public static final int NET_TYPE_NULL = 0;
    public static final int NET_TYPE_WIFI = 1;
    public static final int NET_TYPE_2G = 2;
    public static final int NET_TYPE_3G = 3;
    public static final int NET_TYPE_4G = 4;
    public static final int NET_TYPE_5G = 5;

    private int getCurrentNetTypeInt(Context context) {
        int type = NET_TYPE_NULL;
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm == null) return type;

        NetworkInfo info = cm.getActiveNetworkInfo();
        if (info != null) {
            if (info.getType() == ConnectivityManager.TYPE_WIFI) {
                type = NET_TYPE_WIFI;
            } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
                switch (info.getSubtype()) {
                    case TelephonyManager.NETWORK_TYPE_GPRS:
                    case TelephonyManager.NETWORK_TYPE_EDGE:
                    case TelephonyManager.NETWORK_TYPE_CDMA:
                    case TelephonyManager.NETWORK_TYPE_1xRTT:
                    case TelephonyManager.NETWORK_TYPE_IDEN:
                        type = NET_TYPE_2G;
                        break;

                    case TelephonyManager.NETWORK_TYPE_UMTS:
                    case TelephonyManager.NETWORK_TYPE_EVDO_0:
                    case TelephonyManager.NETWORK_TYPE_EVDO_A:
                    case TelephonyManager.NETWORK_TYPE_HSDPA:
                    case TelephonyManager.NETWORK_TYPE_HSUPA:
                    case TelephonyManager.NETWORK_TYPE_HSPA:
                    case TelephonyManager.NETWORK_TYPE_EVDO_B:
                    case TelephonyManager.NETWORK_TYPE_EHRPD:
                    case TelephonyManager.NETWORK_TYPE_HSPAP:
                        type = NET_TYPE_3G;
                        break;
                    case TelephonyManager.NETWORK_TYPE_LTE:
                        type = NET_TYPE_4G;
                        break;
                    case TelephonyManager.NETWORK_TYPE_NR:
                        type = NET_TYPE_5G;
                        break;
                    default:
                        type = NET_TYPE_WIFI;
                        break;
                }
            }
        }
        return type;
    }

    public static String getIPAddress(Context context) {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm == null) return null;

        NetworkInfo info = cm.getActiveNetworkInfo();
        if (info != null && info.isConnected()) {
            if (info.getType() == ConnectivityManager.TYPE_MOBILE) {//当前使用移动网络
                try {
                    //Enumeration<NetworkInterface> en=NetworkInterface.getNetworkInterfaces();
                    for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                        NetworkInterface intf = en.nextElement();
                        for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                            InetAddress inetAddress = enumIpAddr.nextElement();
                            if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
                                return inetAddress.getHostAddress();
                            }
                        }
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                }
            } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {//当前使用无线网络
                WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
                if (wifiManager != null) {
                    @SuppressLint("MissingPermission") WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                    return intIP2StringIP(wifiInfo.getIpAddress());
                } else
                    return "0.0.0.0";
            }
        }
        return "0.0.0.0";
    }

    /**
     * 将得到的int类型的IP转换为String类型
     */
    private static String intIP2StringIP(int ip) {
        return (ip & 0xFF) + "." +
                ((ip >> 8) & 0xFF) + "." +
                ((ip >> 16) & 0xFF) + "." +
                (ip >> 24 & 0xFF);
    }

    /**
     * 获取网络的mac地址
     *
     * @return xx:xx:xx:xx:xx
     */
    private String getMacAddress() {
        try {
            List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface nif : all) {
                if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
                byte[] macBytes = nif.getHardwareAddress();
                if (macBytes == null) {
                    return null;
                }
                StringBuilder res1 = new StringBuilder();
                for (int i = 0; i < macBytes.length; i++) {
                    if (i == macBytes.length - 1) {
                        res1.append(String.format("%02x", macBytes[i]));
                    } else {
                        res1.append(String.format("%02x:", macBytes[i]));
                    }
                }
                return res1.toString();
            }
        } catch (Exception ignored) {
        }
        return null;
    }
}
